package org.springframework.factory;

import org.dom4j.util.StringUtils;
import org.springframework.annotation.Autowired;
import org.springframework.annotation.Controller;
import org.springframework.annotation.Service;
import org.springframework.parser.SpringConfigParser;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class ClassPathXmlApplicationContext {
    private String springConfig;
    // 存储扫描到的.class文件所有的类路径
    private List<String> classPaths = new ArrayList<>();
    // 存储springIOC容器 key: 对象实现的接口 value: 实现这个接口的对象
    private Map<Class<?>,List<Object>> iocInterfaces = new ConcurrentHashMap<>();
    // 存储ioc的扫描对象(ioc容器) key: 被扫描的class value: 这个类对应的对象
    private Map<Class<?>,Object> iocContainer = new ConcurrentHashMap<>();
    // 存储ioc的扫描对象(ioc容器) key: 对象的名字 value: 这个类对应的对象
    private Map<String,Object> iocNameContainer = new ConcurrentHashMap<>();
    public ClassPathXmlApplicationContext(String springConfig) {
        this.springConfig = springConfig;
        // 初始化springIOC容器
        this.init();
    }

    /**
     * 初始化springIOC容器
     */
    private void init() {
        // 1.调用xml解析方法解析applicationContext.xml获取要扫描的包
        String componentScanPackage = SpringConfigParser.getComponentScanPackage(springConfig);
        System.out.println("解析applicationContext.xml获取要扫描的包是: " + componentScanPackage);
        // 2.扫描这个包下面的类(加载这个包下面的所有类)
        this.loadClasses(componentScanPackage);
        System.out.println("classPath: " + classPaths);
        // 3.执行类的实例化,使用反射给扫描的类生成对象
        this.doInitInstance();
        // 4.实现ioc依赖注入di
        this.doDI();
        System.out.println("实现了接口的类的集合: " + iocInterfaces);
        System.out.println("ioc对象集合: " + iocContainer);
        System.out.println("ioc对象名字集合: " + iocNameContainer);

    }

    /**
     * 实现IOC依赖注入DI
     */
    private void doDI() {
        Set<Class<?>> classes = iocContainer.keySet();
        for (Class<?> aClass : classes) {
            // 反射拿到所有属性
            Field[] declaredFields = aClass.getDeclaredFields();
            if(declaredFields!=null){
                for (Field declaredField : declaredFields) {
                    boolean annotationPresent = declaredField.isAnnotationPresent(Autowired.class);
                    if(annotationPresent){
                        // 如果这个属性有Autowired注解修饰
                        Autowired autowiredAnnotation = declaredField.getAnnotation(Autowired.class);
                        String objectName = autowiredAnnotation.value();
                        Object bean = null;
                        if(!"".equals(objectName)){
                            bean = this.getBean(objectName);
                            if(bean == null){
                                throw new RuntimeException("No bean named '" + bean +"'available !!!!!!!!");
                            }
                        }else{
                            // Autowired value没有写属性 根据类型来找
                            Class<?> type = declaredField.getType();
                            // 根据属性的本类去找 当前类去找
                            bean = this.getBean(type);
                            if ( bean == null ) {
                                // 根据属性的类型去找
                                bean = this.getBeanByInterface(type);
                                if (bean == null) {
                                    throw new RuntimeException();
                                }
                            }
                        }
                        try {
                            // 对象找到了 ,需要依赖注入的对象找到了暴力发射给属性赋值
                            declaredField.setAccessible(true);
                            declaredField.set(iocContainer.get(aClass),bean);
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        }
    }

    /**
     * 根据对象名字获取对象
     * @param objetName
     * @return
     */
    public Object getBean(String objetName){
        if(iocNameContainer.containsKey(objetName)){
            return iocNameContainer.get(objetName);
        }
        return null;
    }


    /**
     * 根据iocContainer本类获取对象
     * @param c
     * @return
     */
    public Object getBean(Class<?> c){
        if (iocContainer.containsKey(c)) {
            return iocContainer.get(c);
        }
        return null;
    }
    /**
     * 根据接口类型自动注入
     * @param c
     * @return
     */
    public Object getBeanByInterface(Class<?> c){
        List<Object> objects = iocInterfaces.get(c);
        if(objects == null){
            return null;
        }
        if(objects.size()>1){
            throw new RuntimeException("我们期待一个但是有两个对象");
        }
        return objects.get(0);
    }

    /**
     * 执行类的实例化,使用反射给扫描的类生成对象
     */
    private void doInitInstance() {
        try {
            for (String classPath : classPaths) {
                Class<?> c = Class.forName(classPath);
                // 判断是否是被@Service或者@Controller注解修饰
                if (c.isAnnotationPresent(Service.class) || c.isAnnotationPresent(Controller.class)) {
                    // 调用无参的构造方法生成对象
                    Object o = c.newInstance();
                    // 反射这个类实现的接口,这个类实现了哪些个接口
                    Class<?>[] interfaces = c.getInterfaces();
                    if (interfaces != null) {
                        for (Class<?> anInterface : interfaces) {
                            List<Object> objects = iocInterfaces.get(anInterface);
                            if (objects == null) {
                                List<Object> object = new ArrayList<>();
                                object.add(o);
                                iocInterfaces.put(anInterface,object);
                            }else {
                                objects.add(o);
                            }
                        }
                    }
                    // 把扫描对象存储到ioc容器中
                    iocContainer.put(c,o);
                    // 要么有@Controller要么有@Service注解
                    Controller controllerAnnotation = c.getAnnotation(Controller.class);
                    Service serviceAnnotation = c.getAnnotation(Service.class);
                    if ( controllerAnnotation != null || serviceAnnotation != null) {
                        // 注解对应的value值
                        String value = null;
                        if ( controllerAnnotation != null ) {
                            value = controllerAnnotation.value();
                        } else if ( serviceAnnotation != null) {
                            value = serviceAnnotation.value();
                        }
                        String objectNme = ""; // 对象的名子
                        if ( "".equals(value) ) {
                            // controller注解或者service没有value属性,默认对象的名字就是类名首字母小写
                            String className = c.getSimpleName();
                            objectNme = String.valueOf(className.charAt(0)).toLowerCase() + className.substring(1);
                        }else {
                            // 注解有value属性
                            objectNme = value;
                        }
                        // 把对象存储起来
                        if ( iocNameContainer.containsKey(objectNme)) {
                            // 表示这个集合中已经有这个名字
                            throw new RuntimeException("集合中该对象已经存在: " + objectNme);
                        }
                        iocNameContainer.put(objectNme,o);
                    }

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {

        }
    }

    /**
     * 加载扫描包下的所有的类 com.burce
     * @param componentScanPackage
     */
    private void loadClasses(String componentScanPackage) {
        URL url = Thread.currentThread().getContextClassLoader().getResource("");
        componentScanPackage = componentScanPackage.replace(".", "/");
        String classPath = url.toString().replace("file:/", "");
        if(classPath.contains("test-classes")){
            classPath = classPath.replace("test-classes","classes");
        }
        classPath = classPath + componentScanPackage;
        System.out.println(classPath);
        // 递归扫描指定路径下的所有classes
        findAllClasses(new File(classPath));
    }

    /**
     * 递归扫描指定路径下的所有classes
     * @param file
     */
    private void findAllClasses(File file) {
        File[] files = file.listFiles();
        for (File file1 : files) {
            if(!file1.isDirectory()){
                // 表是不是目录
                String fileName = file1.getName();
                if (fileName.endsWith(".class")) {
                    // 获取class文件的路径
                    String path = file1.getPath();
                    System.out.println(path);
                    // 解析class文件的路径
                    // D:\hand-write-ssm\hand-write-spring\target\classes\com\burce\service\UserService.class
                    classPaths.add(this.handleClassPath(path));

                }
            }else{
                this.findAllClasses(file1);
            }
        }
    }

    /**
     * D:\hand-write-ssm\hand-write-spring\target\classes\com\burce\service\UserService.class
     * 把字符串转化为 com.burce.service.UserService
     * @param path
     * @return
     */
    private String handleClassPath(String path){
        int index = path.indexOf("classes\\");
        path = path.substring(index + 8, path.length() - 6);
        return path.replace("\\",".");
    }

}
